use std::{error::Error, fs, env};

// 12.3内容
// 定义配置方法返回的数据结构
pub struct Config {
  pub query:String,
  pub filename:String,
  pub case_sensitive:bool
}

impl Config {
  // 解析参数
  // 切片的类型: &[String]和&Vec<String>都行
  pub fn new(args: &Vec<String>) -> Result<Config, &'static str> {
      // 12.4处理错误:
      // 1.将返回值从Config变为Result
      if args.len() < 3 {
          // panic!("错误")
          return Err("你这长度有问题");
      }
      // 为了不夺取args的所有权, 这里使用了借用的方式
      let query = args[1].clone(); // args是借用类型, 但args[1]可不是!
      let filename = args[2].clone();
      // CASE_INSENSITIVE环境变量, 如果存在, 就不区分大小写
      // 如果不存在, 那就是err, 也就是true, 那就区分大小写
      let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
      println!("bool: {}", case_sensitive);
      Ok(Config {
          query,
          filename,
          case_sensitive
      })
  }
}

// 12.5内容, 将读取动作取出
// pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
//   // 这里的问号表示, 有错就将错误返回给调用者
//   let contents = fs::read_to_string(config.filename)?;
//   println!("contents is {:?}", contents);
//   // 12.6, 将search加入到run中
//   for line in search(&config.query, &contents) {
//     // 可以看到执行结果:
//     println!("line有的是: {}", line);
//   }
//   Ok(())
// }

// 12.7改造run
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
  // 这里的问号表示, 有错就将错误返回给调用者
  let contents = fs::read_to_string(config.filename)?;
  println!("contents is {:?}", contents);
  // 如果区分大小写, 就用原先的search方法
  // 这里的results是一个Vec<&str>类型, 是search方法或者search_insensitive方法返回的
  let results = if config.case_sensitive {
    search(&config.query, &contents)
  // 不区分的话, 就用search_insensitive方法
  } else {
    search_insensitive(&config.query, &contents)
  };
  // 此时如果输入: CASE_INSENSITIVE=1 cargo run HelLo demo.txt, 那么就会调用search_insensitive方法
  // 12.6, 将search加入到run中
  for line in results {
    // 可以看到执行结果:
    println!("line有的是: {}", line);
  }
  Ok(())
}

// 12.6内容
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
  // 注意要赋值一个Vec::new();
  let mut result:Vec<&str> = Vec::new();
  for line in contents.lines() {
    if line.contains(query) {
      result.push(line)
    }
  }
  println!("结果是: {:?}", result);
  result
}

// 12.7内容
pub fn search_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
  // 创建一个拥有所有权的字符串
  // 全部改为小写
  let query = query.to_lowercase();

  let mut result:Vec<&str> = Vec::new();
  for line in contents.lines() {
    // 将line全部改为小写
    if line.to_lowercase().contains(&query) {
      result.push(line)
    }
  }
  println!("结果是: {:?}", result);
  result
}

// 12.6 TDD测试驱动开发
#[cfg(test)]
mod tests {
  use super::*;
  #[test]
  fn on_result () {
    let query = "duct";
    let contents = "rust: 
safe, fast, productive
     . Pick three";
    assert_eq!(vec!["safe, fast, productive"], search(query, contents))
  }
#[test]
// 12.7 不区分大小写
  fn case_sensitive() {
    let query = "rUsT";
    let contents = "\
    Rust:
safe, fast, productive.
Pick three.
    Duct tape";
    assert_eq!(vec!["Rust:"], search_insensitive(query, contents));
  }
}